#include "VEnginePCH.h"
#include "VRTTClass.h"
namespace rtti
{
	static VRTTClass* g_RTClassHeader = NULL;
	VNameMap<VRTTClass*>* g_RTClassMap = NULL;
	
	class VRTTClassIteratorImp : public VRTTClassIterator
	{
		virtual void Release()
		{
			delete this;
		}
		virtual VRTTClass* First()
		{
			if (g_RTClassMap)
			{
				itr = g_RTClassMap->begin();
				if (itr != g_RTClassMap->end())
				{
					return itr->second;
				}
			}
			return NULL;
		}
		virtual VRTTClass* Next()
		{
			if (g_RTClassMap)
			{
				itr++;
				if (itr != g_RTClassMap->end())
				{
					return itr->second;
				}
			}
			return NULL;
		}
		VNameMap<VRTTClass*>::iterator itr;
	public:
		VRTTClassIteratorImp()
		{
			if (g_RTClassMap)
			{
				itr = g_RTClassMap->begin();
			}
		}
	};

}
using namespace rtti;

VRTTClass::VRTTClass(VRTTClass* parentCls,  const char* name, VRTTObjectCreateFunc CreateFn, UINT _ClassFlags)
	:ParentClass(parentCls),ClassName(name),RTTCreateObject(CreateFn),ClassFlag(_ClassFlags)
{
	this->NextClass = g_RTClassHeader;
	g_RTClassHeader = this;
}


//////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////
VRTTClass VRTTObject::_RTClassInfo(NULL,"VRTTObject", VRTTObject::CreateObject, VRTCLS_PRIVATE);
VRTTClass* VRTTObject::GetStaticClass() { return &VRTTObject::_RTClassInfo;} 
VRTTClass* VRTTObject::GetParentStaticClass() { return NULL;}
VRTTObject* VRTTObject::CreateObject()	{ return NULL; }	
const VRTTClass* VRTTObject::GetClass() const{ return &VRTTObject::_RTClassInfo;}

VRTTObject* VRTTObject::CastTo(VRTTObject* Object, const VRTTClass* Class)
{
	if (Object == NULL)
	{
		return NULL;
	}
	const VRTTClass* pThisCls = Object->GetClass();
	if (Class->IsBaseOf(pThisCls))
	{
		return Object;
	}
	return NULL;
}

VRTTClass* VRTTObject::FindClass(const char* _ClassName)
{
	VASSERT(g_RTClassMap);
	VName ClassName(_ClassName);
	VNameMap<VRTTClass*>::const_iterator itr = g_RTClassMap->find(ClassName);
	if (itr != g_RTClassMap->end())
	{
		return (VRTTClass*)itr->second;
	}
	return NULL;
}

BOOL VRTTObject::StaticInit()
{
	VASSERT(g_RTClassMap == NULL);
	g_RTClassMap = new VNameMap<VRTTClass*>;
	g_RTClassMap->clear();
	VRTTClass* Class = g_RTClassHeader;
	VName className;
	while (Class)
	{
		className = Class->GetClassName();
		g_RTClassMap->insert(VNameMap<VRTTClass*>::value_type(className, Class));
		Class = Class->GetNextClass();
	}
	return TRUE;
}

void VRTTObject::StaticShutdown()
{
	if (g_RTClassMap)
	{
		g_RTClassMap->clear();
		delete g_RTClassMap;
		g_RTClassMap = NULL;
	}
}

void VRTTObject::DeleteThis()
{
	delete this;
}

BOOL VRTTObject::Load(VIOStream* InputStream)
{
	return TRUE;
}

BOOL VRTTObject::Save(VIOStream* OutputStream)
{
	return TRUE;
}

VRTTObject* VRTTObject::LoadObject(VIOStream* InputStream)
{
	WORD ClassNameLen;
	static char sClassName[256]; VMT_SAFE_WARN;
	if (InputStream->Read(&ClassNameLen, sizeof(WORD)) != sizeof(WORD))
	{
		return NULL;
	}
	if (ClassNameLen >= 255)
	{
		return NULL;
	}
	if (InputStream->Read(sClassName, ClassNameLen) != ClassNameLen)
	{
		return NULL;
	}
	sClassName[ClassNameLen] = 0;

	UINT ChunkSize = 0;
	if (InputStream->Read(&ChunkSize, sizeof(UINT)) != sizeof(UINT))
	{
		return NULL;
	}
	VRTTClass* RTClass = FindClass(sClassName);
	if (!RTClass)
	{
		VLOG("can't find class %s , serialize failed!", sClassName);
		return NULL;
	}
	VRTTObject* Object = RTClass->CreateObject();
	if (!Object)
	{
		VLOG("not enough memory.");
		return NULL;
	}
	
	UINT CurPos = InputStream->GetCurPos();
	CurPos += ChunkSize;
	if (Object->Load(InputStream))
	{
		UINT EndPos = InputStream->GetCurPos();
		if (CurPos != EndPos)
		{
			Object->DeleteThis();
			Object = NULL;
			InputStream->SeekTo(CurPos);
		}
		return Object;
	}
	Object->DeleteThis();
	InputStream->SeekTo(CurPos);
	return NULL;
}

BOOL VRTTObject::SaveObject(VRTTObject* Object, VIOStream* OutputStream)
{
	VASSERT(Object && OutputStream);
	// save chunk.
	UINT StartPos = OutputStream->GetCurPos();
	// save class runtime info
	const VRTTClass* Class = Object->GetClass();
	VASSERT(Class);
	const char* Name = Class->GetClassName();
	WORD NameLen = (WORD)strlen(Name);
	OutputStream->Write(&NameLen, sizeof(WORD));
	OutputStream->Write(Name, NameLen);
	UINT ObjectChunkSize = 0;
	OutputStream->Write(&ObjectChunkSize, sizeof(UINT));

	UINT ObjectPos = StartPos + sizeof(WORD) + NameLen + sizeof(UINT);
	if (Object->Save(OutputStream))
	{
		// rewrite chunk size
		StartPos = OutputStream->GetCurPos();
		OutputStream->SeekTo(ObjectPos - sizeof(UINT));
		ObjectChunkSize = StartPos - ObjectPos;
		OutputStream->Write(&ObjectChunkSize, sizeof(UINT));
		// restore to end .
		OutputStream->SeekTo(StartPos);
		return TRUE;
	}
	OutputStream->SeekTo(StartPos);
	return FALSE;
}